package listeners

import (
	"blockchains-aio/fabric/ca"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
)

func RegisterMsp(router *gin.Engine) {
	rootGroup := getRootGroup(router)
	msp := rootGroup.Group("/msp")

	msp.POST("/identity/:orgId", callMethod)
	msp.DELETE("/identity/:orgId", callMethod)

	msp.POST("/identity/asset", addAssetIdentity)
	msp.DELETE("/identity/asset", removeAssetIdentity)
	msp.GET("/identity/asset", getIdentityAsset)

}

var (
	holdAssets = make(map[string]Asset)
	holdApps   = make(map[string]App)

	resources = Resource{
		Assets: []Asset{
			{
				Type:   "peer",
				Name:   "peer resource",
				Amount: 1000,
			},
			{
				Type:   "admin",
				Name:   "admin resource",
				Amount: 1000,
			},
		},
		App: App{
			Type:      "user",
			Name:      "capability",
			Interface: "getBirthday,getCards",
		},
	}
)

type Resource struct {
	Assets []Asset `json:"assets"`
	App    App     `json:"app"`
}

type Asset struct {
	Type   string `json:"type"`
	Name   string `json:"name"`
	Amount int    `json:"amount"`
}

func (asset Asset) String() string {
	assetBytes, _ := json.Marshal(asset)
	return string(assetBytes)
}

type App struct {
	Type      string `json:"type"`
	Name      string `json:"name"`
	Interface string `json:"interface"`
}

func (app App) String() string {
	appBytes, _ := json.Marshal(app)
	return string(appBytes)
}

func getIdentityAsset(ctx *gin.Context) {
	userId := ctx.Query("userId")

	asset, ok := holdAssets[userId]
	if !ok {
		app, ok := holdApps[userId]

		if !ok {
			ctx.JSON(http.StatusBadRequest, NewErrorMessage(Msp, 1, "getIdentityAsset", fmt.Sprintf("non-existed user: %s", userId)))
			return
		}

		ctx.JSON(http.StatusOK, app.String())
	} else {
		ctx.JSON(http.StatusOK, asset.String())
	}
}

func removeAssetIdentity(ctx *gin.Context) {
	userId := ctx.Query("userId")

	delete(holdAssets, userId)
	delete(holdApps, userId)

	ctx.JSON(http.StatusOK, "")
}

func addAssetIdentity(ctx *gin.Context) {
	t := ctx.Query("type")
	userId := ctx.Query("userId")
	if userId == "" || t == "" {
		ctx.JSON(http.StatusBadRequest, NewErrorMessage(Msp, 1, "addAssetIdentity", fmt.Sprintf("bad paramters: type and userId")))
		return
	}

	if t == "peer" {
		holdAssets[userId] = resources.Assets[0]
	} else if t == "admin" {
		holdAssets[userId] = resources.Assets[1]
	} else {
		holdApps[userId] = resources.App
	}

	ctx.JSON(http.StatusOK, "")
}

func callMethod(ctx *gin.Context) {
	var callMethodSpec CallMethodSpec
	orgId := ctx.Param("orgId")
	fcc := ca.NewCooridnator("", orgId)

	if err := ctx.BindJSON(&callMethodSpec); err != nil {
		ctx.JSON(http.StatusBadRequest, fmt.Sprintf("%v", err))
		return
	}

	params := make([]interface{}, 0)
	for _, v := range callMethodSpec.Params {
		params = append(params, v)
	}
	if err := fcc.InvokeMethod(callMethodSpec.Method, params...); err != nil {
		ctx.JSON(http.StatusBadRequest, fmt.Sprintf("%v", err))
		return
	}

	ctx.JSON(http.StatusOK, "")
	return
}

type CallMethodSpec struct {
	Method string     `json:"method"`
	Params []ca.Flags `json:"params"`
}
